package com.example.gymlearning.controller;

import com.example.gymlearning.dto.KnowledgePackageDTO;
import com.example.gymlearning.dto.KnowledgePointDTO;
import com.example.gymlearning.dto.LearningNoteRequest;
import com.example.gymlearning.dto.LearningQuestionRequest;
import com.example.gymlearning.dto.LearningSessionRequest;
import com.example.gymlearning.dto.LearningTimeRequest;
import com.example.gymlearning.dto.response.UserResponse;
import com.example.gymlearning.dto.QuizSummaryRequest;
import com.example.gymlearning.entity.KnowledgeClass;
import com.example.gymlearning.entity.KnowledgeModule;
import com.example.gymlearning.entity.KnowledgePoint;
import com.example.gymlearning.entity.LearningNote;
import com.example.gymlearning.entity.LearningQuestion;
import com.example.gymlearning.entity.LearningRecord;
import com.example.gymlearning.entity.LearningStageRecord;
import com.example.gymlearning.entity.LearningQuizRecord;
import com.example.gymlearning.entity.LearningTask;
import com.example.gymlearning.entity.UserPointProgress;
import com.example.gymlearning.entity.UserPackageProgress;
import com.example.gymlearning.enums.LearningRecordStatus;
import com.example.gymlearning.service.KnowledgeClassService;
import com.example.gymlearning.service.KnowledgeModuleService;
import com.example.gymlearning.service.KnowledgePackageService;
import com.example.gymlearning.service.KnowledgePointService;
import com.example.gymlearning.service.LearningContentService;
import com.example.gymlearning.service.LearningNoteService;
import com.example.gymlearning.service.LearningQuestionService;
import com.example.gymlearning.service.LearningRecordService;
import com.example.gymlearning.service.LearningStageRecordService;
import com.example.gymlearning.service.LearningQuizRecordService;
import com.example.gymlearning.service.QuizQuestionService;
import com.example.gymlearning.service.QuestionRandomizationService;
import com.example.gymlearning.service.UserProgressService;
import com.example.gymlearning.service.UserWrongQuestionService;
import com.example.gymlearning.dto.QuizQuestionDTO;
import com.example.gymlearning.entity.UserWrongQuestion;
import com.example.gymlearning.dto.WrongQuestionRequest;
import com.github.pagehelper.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Controller
@RequestMapping("/knowledge")
@Slf4j
public class KnowledgeController {

    private static final Set<String> SUPPORTED_STAGE_TYPES = new HashSet<>(Arrays.asList(
            "concept", "analogy", "example", "overview", "usage", "practice", "coding", "quiz", "visualization", "general", "reflection", "warmup"
    ));

    @Resource
    private KnowledgeClassService knowledgeClassService;

    @Resource
    private KnowledgeModuleService knowledgeModuleService;

    @Resource
    private KnowledgePackageService knowledgePackageService;

    @Resource
    private KnowledgePointService knowledgePointService;

    @Resource
    private LearningContentService learningContentService;

    @Resource
    private QuizQuestionService quizQuestionService;

    @Resource
    private LearningRecordService learningRecordService;

    @Resource
    private LearningStageRecordService learningStageRecordService;

    @Resource
    private LearningQuizRecordService learningQuizRecordService;

    @Resource
    private LearningQuestionService learningQuestionService;

    @Resource
    private LearningNoteService learningNoteService;

    @Resource
    private UserProgressService userProgressService;

    @Resource
    private UserWrongQuestionService userWrongQuestionService;

    @Resource
    private QuestionRandomizationService questionRandomizationService;

    /**
     * 知识训练馆主页面 - 支持四个层级导航
     */
    @GetMapping("/gym")
    public String knowledgeGym(@RequestParam(required = false) Long classId,
                               @RequestParam(required = false) Long moduleId,
                               @RequestParam(required = false) Long packageId,
                               @RequestParam(required = false) String difficulty,
                               @RequestParam(defaultValue = "1") int page,
                               @RequestParam(defaultValue = "6") int size,
                               Model model, HttpSession session) {

        log.info("===访问知识训练馆===");
        log.info("分类ID: {}, 模块ID: {}, 知识包ID: {}, 难度: {}, 页码: {}, 每页大小: {}",
                classId, moduleId, packageId, difficulty, page, size);

        // 1. 用户信息和层级信息
        Object user = session.getAttribute("user");
        model.addAttribute("user", user);

        // 加载知识分类列表
        List<KnowledgeClass> knowledgeClasses = knowledgeClassService.getAllClasses();
        model.addAttribute("knowledgeClasses", knowledgeClasses);
        log.info("加载知识分类数量: {}", knowledgeClasses.size());

        // 根据分类ID加载知识板块
        List<KnowledgeModule> modules;
        if (classId != null) {
            modules = knowledgeModuleService.getModulesByClassId(classId);
            log.info("按分类筛选，分类ID: {}, 模块数量: {}", classId, modules.size());
        } else {
            modules = knowledgeModuleService.getAllModules();
            log.info("加载所有模块，数量: {}", modules.size());
        }
        model.addAttribute("modules", modules);

        // 2. 分页查询知识包 - 支持多层级筛选
        Page<KnowledgePackageDTO> knowledgePackagePage;

        if (packageId != null) {
            // 如果指定了具体知识包，只显示该知识包
            knowledgePackagePage = knowledgePackageService.getPackageById(packageId, page, size);
            log.info("按知识包筛选，知识包ID: {}", packageId);
        } else if (moduleId != null) {
            // 如果指定了模块，显示该模块下的知识包
            knowledgePackagePage = knowledgePackageService.getPackagesByModule(moduleId, page, size);
            log.info("按模块筛选，模块ID: {}", moduleId);
        } else if (classId != null) {
            // 如果指定了分类，显示该分类下的知识包
            knowledgePackagePage = knowledgePackageService.getPackagesByClass(classId, page, size);
            log.info("按分类筛选，分类ID: {}", classId);
        } else {
            // 默认显示所有知识包
            knowledgePackagePage = knowledgePackageService.getAllPackagesWithStats(page, size);
            log.info("加载所有知识包");
        }

        // 3. 添加分页相关信息到模型
        model.addAttribute("knowledgePackages", knowledgePackagePage.getResult());
        model.addAttribute("currentPage", knowledgePackagePage.getPageNum());
        model.addAttribute("totalPages", knowledgePackagePage.getPages());
        model.addAttribute("totalItems", knowledgePackagePage.getTotal());
        model.addAttribute("pageSize", size);

        // 传递当前层级ID，用于分页时保持筛选状态
        model.addAttribute("currentClassId", classId);
        model.addAttribute("currentModuleId", moduleId);
        model.addAttribute("currentPackageId", packageId);
        model.addAttribute("currentDifficulty", difficulty);

        log.info("当前页: {}, 总页数: {}, 知识包数量: {}",
                knowledgePackagePage.getPageNum(),
                knowledgePackagePage.getPages(),
                knowledgePackagePage.getResult().size());

        return "knowledge-gym-4";
    }

    /**
     * 记录随堂测验结果
     */
    @PostMapping("/training/{pointId}/quiz-summary")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> recordQuizSummary(@PathVariable Long pointId,
                                                                 @RequestBody QuizSummaryRequest payload,
                                                                 HttpServletRequest request) {
        Map<String, Object> response = new HashMap<>();
        if (payload == null || payload.getRecordId() == null) {
            response.put("success", false);
            response.put("message", "recordId不能为空");
            return ResponseEntity.badRequest().body(response);
        }

        try {
            LearningRecord record = learningRecordService.queryById(payload.getRecordId());
            if (record == null) {
                response.put("success", false);
                response.put("message", "学习会话不存在");
                return ResponseEntity.badRequest().body(response);
            }

            UserResponse user = getSessionUser(request.getSession());
            if (user != null && record.getUserId() != null && !record.getUserId().equals(user.getId())) {
                response.put("success", false);
                response.put("message", "无权记录该学习会话测验结果");
                return ResponseEntity.status(403).body(response);
            }

            KnowledgePointDTO point = knowledgePointService.getPointDetail(pointId);
            Long packageId = point != null ? point.getPackageId() : null;

            record.setPointId(record.getPointId() == null ? pointId : record.getPointId());
            record.setTotalQuestions(payload.getTotalQuestions());
            record.setCorrectCount(payload.getCorrectCount());
            record.setIncorrectCount(payload.getIncorrectCount());
            record.setTotalScore(payload.getScore());
            record.setQuizCompleted(true);
            record.setStatus(LearningRecordStatus.COMPLETED.getDbValue());
            record.setEndTime(new Date());
            learningRecordService.update(record);

            if (payload.getAnswers() != null && !payload.getAnswers().isEmpty()) {
                List<LearningQuizRecord> answers = payload.getAnswers().stream().map(item -> {
                    LearningQuizRecord entity = new LearningQuizRecord();
                    entity.setRecordId(record.getId());
                    entity.setQuestionId(item.getQuestionId());
                    entity.setQuestionType(item.getQuestionType());
                    entity.setUserAnswer(item.getUserAnswer());
                    entity.setCorrectAnswer(item.getCorrectAnswer());
                    entity.setIsCorrect(item.getCorrect());
                    entity.setTimeSpentSeconds(item.getTimeSpentSeconds());
                    entity.setCreatedTime(new Date());
                    return entity;
                }).collect(Collectors.toList());
                learningQuizRecordService.insertBatch(answers);

                Long userId = record.getUserId();
                if (userId != null && packageId != null) {
                    payload.getAnswers().forEach(item -> {
                        if (Boolean.FALSE.equals(item.getCorrect())) {
                            userWrongQuestionService.addWrongQuestion(userId,
                                    item.getQuestionId(),
                                    pointId,
                                    packageId,
                                    item.getUserAnswer());
                        } else {
                            userWrongQuestionService.updatePracticeRecord(userId, item.getQuestionId(), true);
                        }
                    });
                }
            }

            response.put("success", true);
            response.put("recordId", record.getId());
            response.put("score", payload.getScore());
            response.put("correct", payload.getCorrectCount());
            response.put("incorrect", payload.getIncorrectCount());
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("记录随堂测验失败，pointId: {}", pointId, e);
            response.put("success", false);
            response.put("message", "记录随堂测验失败: " + e.getMessage());
            return ResponseEntity.status(500).body(response);
        }
    }

    /**
     * 创建学习会话，用于关联阶段和随堂测验记录
     */
    @PostMapping("/training/{pointId}/sessions")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> createLearningSession(@PathVariable Long pointId,
                                                                     @RequestBody(required = false) LearningSessionRequest payload,
                                                                     HttpServletRequest request) {
        Map<String, Object> response = new HashMap<>();
        UserResponse user = getSessionUser(request.getSession());
        if (user == null) {
            response.put("success", false);
            response.put("message", "请先登录");
            return ResponseEntity.status(401).body(response);
        }

        try {
            LearningRecord record = new LearningRecord();
            record.setUserId(user.getId());
            record.setTaskId(pointId);
            record.setPointId(pointId);
            record.setStatus(LearningRecordStatus.IN_PROGRESS.getDbValue());
            record.setStartTime(new Date());
            record.setCreatedTime(new Date());
            record.setActualDuration(0);
            record.setCurrentStageIndex(0);
            record.setQuizCompleted(false);
            if (payload != null && payload.getStageCount() != null) {
                record.setTotalStageCount(payload.getStageCount());
            }
            learningRecordService.insert(record);

            response.put("success", true);
            response.put("recordId", record.getId());
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("创建学习会话失败，pointId: {}", pointId, e);
            response.put("success", false);
            response.put("message", "创建学习会话失败: " + e.getMessage());
            return ResponseEntity.status(500).body(response);
        }
    }

    /**
     * 开始学习知识点 - 主入口
     */
    @GetMapping("/training/start/{pointId}")
    public String startTraining(@PathVariable Long pointId, Model model, HttpServletRequest request) {
        try {
            log.info("开始学习知识点: {}", pointId);

            // 获取知识点信息
            KnowledgePointDTO point = knowledgePointService.getPointDetail(pointId);

            if (point == null) {
                model.addAttribute("error", "知识点不存在");
                return "error";
            }

            // 设置模型属性
            model.addAttribute("point", point);
            model.addAttribute("pointId", pointId);

            // 自动判断知识点类型（如果未设置）
            String knowledgeType = determineKnowledgeType(point);
            model.addAttribute("knowledgeType", knowledgeType);

            // 添加用户信息（从session获取）
            Object user = request.getSession().getAttribute("user");
            if (user != null) {
                model.addAttribute("user", user);
            }

            log.info("成功加载知识点: {}, 类型: {}", point.getTitle(), knowledgeType);
            return "learning-engine-final-3";

        } catch (Exception e) {
            log.error("加载学习内容失败", e);
            model.addAttribute("error", "加载学习内容失败: " + e.getMessage());
            return "error";
        }
    }

    /**
     * 获取知识包下的知识点列表
     */
    @GetMapping("/package/{packageId}/points")
    @ResponseBody
    public ResponseEntity<List<KnowledgePointDTO>> getPointsByPackage(@PathVariable Long packageId) {
        log.info("获取知识包 {} 的知识点列表", packageId);
        try {
            List<KnowledgePointDTO> points = knowledgePointService.getPointsByPackageId(packageId);
            return ResponseEntity.ok(points);
        } catch (Exception e) {
            log.error("获取知识点失败", e);
            return ResponseEntity.status(500).body(null);
        }
    }

    /**
     * 获取学习内容API - 增强版，包含所有阶段的内容
     */
    @GetMapping("/training/{pointId}/learning-content")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> getLearningContent(@PathVariable Long pointId) {
        log.info("获取知识点学习内容，ID: {}", pointId);

        try {
            // 优先使用LearningContentService获取内容
            Map<String, Object> content = learningContentService.getLearningContent(pointId);

            // 如果服务返回空，使用动态生成的内容作为备用
            if (content == null || content.isEmpty()) {
                content = generateDynamicLearningContent(pointId);
            }

            return ResponseEntity.ok(content);
        } catch (Exception e) {
            log.error("获取学习内容失败，pointId: {}", pointId, e);
            // 出错时返回动态生成的内容
            Map<String, Object> fallbackContent = generateDynamicLearningContent(pointId);
            return ResponseEntity.ok(fallbackContent);
        }
    }

    /**
     * 获取学习页面测验题目API
     * 使用题组随机化：从每个题组中随机选择一道题，选项随机变化
     * 不限制题目数量，有多少显示多少
     * 随堂检验阶段只显示单选题和判断题，不给学习者太多压力
     */
    @GetMapping("/training/{pointId}/quiz-questions")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> getQuizQuestions(@PathVariable Long pointId) {
        log.info("获取学习页面测验题目，知识点ID: {}", pointId);

        try {
            // 使用随机化服务获取题目：从题组中随机选择，选项随机变化
            List<QuizQuestionDTO> randomizedQuestions = questionRandomizationService.getRandomizedQuestions(pointId, "LEARNING");
            
            // 过滤题目类型：随堂检验只显示单选题和判断题，不给学习者太多压力
            randomizedQuestions = randomizedQuestions.stream()
                    .filter(q -> {
                        String questionType = normalizeQuestionTypeForFilter(q.getQuestionType());
                        return "single".equals(questionType) || "true-false".equals(questionType);
                    })
                    .collect(Collectors.toList());

            // 转换为前端需要的格式（保持兼容性）
            List<Map<String, Object>> questionsForFrontend = randomizedQuestions.stream()
                    .map(dto -> {
                        Map<String, Object> questionMap = new HashMap<>();
                        questionMap.put("id", dto.getId());
                        questionMap.put("knowledgePointId", dto.getKnowledgePointId());
                        questionMap.put("questionType", dto.getQuestionType());
                        questionMap.put("content", dto.getContent());
                        questionMap.put("explanation", dto.getExplanation());
                        questionMap.put("score", dto.getScore());
                        questionMap.put("usageType", dto.getUsageType());
                        questionMap.put("difficulty", dto.getDifficulty());
                        questionMap.put("packageId", dto.getPackageId());

                        // 正确答案：使用correctAnswerValue（实际内容值）
                        questionMap.put("correctAnswer", dto.getCorrectAnswerValue());
                        
                        // 选项：转换为前端期望的格式
                        if (dto.getOptions() != null && !dto.getOptions().isEmpty()) {
                            // 构建options字段（对象格式：{"A": "选项内容"}）
                            // 前端parseQuizOptions会将其解析为 {label: "A", text: "选项内容", value: "A"}
                            // 但我们需要value是选项的实际内容，所以使用数组格式
                            List<Map<String, Object>> optionsList = new ArrayList<>();
                            for (com.example.gymlearning.dto.OptionDTO opt : dto.getOptions()) {
                                Map<String, Object> optMap = new HashMap<>();
                                optMap.put("label", opt.getLabel());
                                optMap.put("text", opt.getValue()); // text显示选项内容
                                optMap.put("value", opt.getValue()); // value是选项的实际内容，用于与correctAnswer匹配
                                optionsList.add(optMap);
                            }
                            // 同时提供对象格式（兼容性）
                            Map<String, String> optionsMap = new HashMap<>();
                            for (com.example.gymlearning.dto.OptionDTO opt : dto.getOptions()) {
                                optionsMap.put(opt.getLabel(), opt.getValue());
                            }
                            questionMap.put("options", optionsMap);
                            questionMap.put("optionsList", optionsList);
                        } else {
                            questionMap.put("options", null);
                            questionMap.put("optionsList", null);
                        }
                        
                        // 填空题：答案同义词
                        if (dto.getAnswerSynonyms() != null && !dto.getAnswerSynonyms().isEmpty()) {
                            questionMap.put("answerSynonyms", dto.getAnswerSynonyms());
                        }
                        
                        // 填空题：多空题答案列表
                        if (dto.getBlankAnswers() != null && !dto.getBlankAnswers().isEmpty()) {
                            questionMap.put("blankAnswers", dto.getBlankAnswers());
                        }
                        
                        return questionMap;
                    })
                    .collect(Collectors.toList());

            Map<String, Object> response = new HashMap<>();
            response.put("success", true);
            response.put("questions", questionsForFrontend);
            response.put("totalCount", randomizedQuestions.size());
            response.put("practiceCount", randomizedQuestions.size());

            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("获取测验题目失败", e);
            Map<String, Object> response = new HashMap<>();
            response.put("success", false);
            response.put("message", "获取题目失败: " + e.getMessage());
            response.put("questions", Collections.emptyList());
            return ResponseEntity.ok(response); // 返回空题目列表，而不是500错误
        }
    }

    /**
     * 获取知识点对应的学习任务编排
     */
    @GetMapping("/training/{pointId}/tasks")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> getLearningTasks(@PathVariable Long pointId) {
        log.info("获取知识点 {} 的学习任务编排", pointId);
        Map<String, Object> response = new HashMap<>();
        try {
            KnowledgePointDTO point = knowledgePointService.getPointDetail(pointId);
            if (point == null) {
                response.put("success", false);
                response.put("message", "知识点不存在");
                return ResponseEntity.ok(response);
            }

            List<LearningTask> tasks = buildTaskPlan(point);
            int totalDuration = tasks.stream()
                    .mapToInt(task -> task.getEstimatedDuration() == null ? 0 : task.getEstimatedDuration())
                    .sum();
            int totalExperience = tasks.stream()
                    .mapToInt(task -> task.getExperienceReward() == null ? 0 : task.getExperienceReward())
                    .sum();

            response.put("success", true);
            response.put("tasks", tasks);
            response.put("totalDuration", totalDuration);
            response.put("totalExperience", totalExperience);
            response.put("knowledgeType", point.getKnowledgeType());
            response.put("difficulty", point.getDifficultyLevel());
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("获取学习任务失败，pointId: {}", pointId, e);
            response.put("success", false);
            response.put("message", "获取学习任务失败: " + e.getMessage());
            return ResponseEntity.status(500).body(response);
        }
    }

    /**
     * 记录学习时长
     */
    @PostMapping("/training/{pointId}/time-tracking")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> recordLearningTime(@PathVariable Long pointId,
                                                                  @RequestBody LearningTimeRequest payload,
                                                                  HttpServletRequest request) {
        Map<String, Object> response = new HashMap<>();
        if (payload == null || payload.getDurationSeconds() == null || payload.getDurationSeconds() <= 0) {
            response.put("success", false);
            response.put("message", "durationSeconds不能为空");
            return ResponseEntity.badRequest().body(response);
        }

        if (payload.getRecordId() == null) {
            response.put("success", false);
            response.put("message", "recordId不能为空，请先创建学习会话");
            return ResponseEntity.badRequest().body(response);
        }

        try {
            LearningRecord record = learningRecordService.queryById(payload.getRecordId());
            if (record == null) {
                response.put("success", false);
                response.put("message", "学习会话不存在");
                return ResponseEntity.badRequest().body(response);
            }

            UserResponse user = getSessionUser(request.getSession());
            if (user != null && record.getUserId() != null && !record.getUserId().equals(user.getId())) {
                response.put("success", false);
                response.put("message", "无权记录该学习会话");
                return ResponseEntity.status(403).body(response);
            }

            Date start = payload.getStartTimestamp() != null ? new Date(payload.getStartTimestamp()) : new Date();
            Date end = payload.getEndTimestamp() != null ? new Date(payload.getEndTimestamp()) : new Date();

            LearningStageRecord stageRecord = new LearningStageRecord();
            stageRecord.setRecordId(record.getId());
            stageRecord.setStageIndex(payload.getStageIndex() == null ? 0 : payload.getStageIndex());
            stageRecord.setStageTitle(payload.getStage());
            stageRecord.setStageType(payload.getType());
            stageRecord.setDurationSeconds(payload.getDurationSeconds());
            stageRecord.setRecommendedSeconds(payload.getRecommendedSeconds());
            stageRecord.setStartTime(start);
            stageRecord.setEndTime(end);
            learningStageRecordService.insert(stageRecord);

            record.setPointId(record.getPointId() == null ? pointId : record.getPointId());
            record.setTaskId(record.getTaskId() == null ? (payload.getTaskId() != null ? payload.getTaskId() : pointId) : record.getTaskId());
            record.setNotes(payload.getStage());
            record.setEndTime(end);
            Integer prevDuration = record.getActualDuration() == null ? 0 : record.getActualDuration();
            record.setActualDuration(prevDuration + payload.getDurationSeconds());
            Integer prevExp = record.getExperienceEarned() == null ? 0 : record.getExperienceEarned();
            record.setExperienceEarned(prevExp + (payload.getExperienceDelta() == null ? 0 : payload.getExperienceDelta()));
            if (payload.getStageIndex() != null) {
                int currentIdx = record.getCurrentStageIndex() == null ? 0 : record.getCurrentStageIndex();
                record.setCurrentStageIndex(Math.max(currentIdx, payload.getStageIndex()));
            }
            if (payload.getTotalStageCount() != null) {
                record.setTotalStageCount(payload.getTotalStageCount());
            }
            if (!Boolean.TRUE.equals(record.getQuizCompleted())) {
                record.setStatus(LearningRecordStatus.IN_PROGRESS.getDbValue());
            }
            learningRecordService.update(record);

            if (record.getUserId() != null) {
                userProgressService.addLearningDuration(record.getUserId(), pointId, payload.getDurationSeconds());
            }

            response.put("success", true);
            response.put("recordId", record.getId());
            response.put("stageRecordId", stageRecord.getId());
            response.put("duration", payload.getDurationSeconds());
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("记录学习时长失败，pointId: {}", pointId, e);
            response.put("success", false);
            response.put("message", "记录学习时长失败: " + e.getMessage());
            return ResponseEntity.status(500).body(response);
        }
    }

    /**
     * 获取学习过程中的问题
     */
    @GetMapping("/training/{pointId}/questions")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> loadLearningQuestions(@PathVariable Long pointId,
                                                                     HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        List<LearningQuestion> items = learningQuestionService.getUserQuestions(user.getId(), pointId);
        Map<String, Object> resp = new HashMap<>();
        resp.put("items", items);
        long pendingCount = items.stream().filter(q -> q.getResolved() == null).count();
        resp.put("pendingCount", pendingCount);
        return ResponseEntity.ok(resp);
    }

    /**
     * 新增学习问题
     */
    @PostMapping("/training/{pointId}/questions")
    @ResponseBody
    public ResponseEntity<?> createLearningQuestion(@PathVariable Long pointId,
                                                    @RequestBody LearningQuestionRequest requestBody,
                                                    HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        if (requestBody == null || requestBody.getQuestionText() == null || requestBody.getQuestionText().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Collections.singletonMap("message", "问题内容不能为空"));
        }

        KnowledgePoint pointEntity = knowledgePointService.getById(pointId);
        KnowledgePointDTO pointDTO = knowledgePointService.getPointDetail(pointId);
        if (pointEntity == null && pointDTO == null) {
            return ResponseEntity.badRequest().body(Collections.singletonMap("message", "知识点不存在"));
        }

        LearningQuestion question = new LearningQuestion();
        question.setUserId(user.getId());
        question.setPointId(pointId);
        question.setPackageId(pointDTO != null ? pointDTO.getPackageId() : null);
        question.setModuleId(pointEntity != null ? pointEntity.getModuleId() : null);
        question.setQuestionText(requestBody.getQuestionText());
        String normalizedStage = normalizeStageType(requestBody.getStageType());
        question.setStageType(normalizedStage);
        // 确保 questionType 值不会过长，防止数据库截断错误
        String questionType = deriveQuestionType(normalizedStage);
        if (questionType != null && questionType.length() > 50) {
            questionType = questionType.substring(0, 50);
            log.warn("questionType 值过长，已截断: {}", questionType);
        }
        question.setQuestionType(questionType);
        question.setAnswerText(requestBody.getAnswerText());
        question.setResolved(null);
        question.setStatus("OPEN");
        Date now = new Date();
        question.setCreatedAt(now);
        question.setUpdatedAt(now);

        LearningQuestion saved = learningQuestionService.createQuestion(question);
        return ResponseEntity.ok(saved);
    }

    /**
     * 更新问题状态
     */
    @PostMapping("/training/questions/{questionId}/resolve")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> updateQuestionStatus(@PathVariable Long questionId,
                                                                    @RequestParam Boolean resolved,
                                                                    HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        boolean updated = learningQuestionService.updateResolved(questionId, user.getId(), resolved);
        return ResponseEntity.ok(Collections.singletonMap("success", updated));
    }

    /**
     * 更新问题内容和答案（解决问题）
     */
    @PutMapping("/training/questions/{questionId}")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> updateQuestion(@PathVariable Long questionId,
                                                              @RequestBody LearningQuestionRequest requestBody,
                                                              HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        if (requestBody == null || requestBody.getQuestionText() == null || requestBody.getQuestionText().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Collections.singletonMap("message", "问题内容不能为空"));
        }
        if (requestBody.getAnswerText() == null || requestBody.getAnswerText().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Collections.singletonMap("message", "答案内容不能为空"));
        }
        boolean updated = learningQuestionService.updateQuestion(questionId, user.getId(),
                requestBody.getQuestionText(), requestBody.getAnswerText());
        if (updated) {
            // 重新获取更新后的问题
            LearningQuestion updatedQuestion = learningQuestionService.getQuestionById(questionId, user.getId());
            Map<String, Object> response = new HashMap<>();
            response.put("success", true);
            response.put("question", updatedQuestion);
            return ResponseEntity.ok(response);
        }
        return ResponseEntity.status(500).body(Collections.singletonMap("message", "更新失败"));
    }

    /**
     * 删除问题
     */
    @DeleteMapping("/training/questions/{questionId}")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> deleteQuestion(@PathVariable Long questionId,
                                                              HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        boolean deleted = learningQuestionService.deleteQuestion(questionId, user.getId());
        return ResponseEntity.ok(Collections.singletonMap("success", deleted));
    }

    /**
     * 获取学习笔记
     */
    @GetMapping("/training/{pointId}/notes")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> loadLearningNotes(@PathVariable Long pointId,
                                                                 HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        List<LearningNote> items = learningNoteService.getUserNotes(user.getId(), pointId);
        Map<String, Object> resp = new HashMap<>();
        resp.put("items", items);
        return ResponseEntity.ok(resp);
    }

    /**
     * 新增学习笔记
     */
    @PostMapping("/training/{pointId}/notes")
    @ResponseBody
    public ResponseEntity<?> createLearningNote(@PathVariable Long pointId,
                                                @RequestBody LearningNoteRequest requestBody,
                                                HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        if (requestBody == null || requestBody.getNoteText() == null || requestBody.getNoteText().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Collections.singletonMap("message", "笔记内容不能为空"));
        }

        KnowledgePoint pointEntity = knowledgePointService.getById(pointId);
        KnowledgePointDTO pointDTO = knowledgePointService.getPointDetail(pointId);
        if (pointEntity == null && pointDTO == null) {
            return ResponseEntity.badRequest().body(Collections.singletonMap("message", "知识点不存在"));
        }

        LearningNote note = new LearningNote();
        note.setUserId(user.getId());
        note.setPointId(pointId);
        note.setPackageId(pointDTO != null ? pointDTO.getPackageId() : null);
        note.setModuleId(pointEntity != null ? pointEntity.getModuleId() : null);
        note.setNoteText(requestBody.getNoteText());
        note.setNoteType(requestBody.getNoteType() == null ? "insight" : requestBody.getNoteType());
        note.setMood(requestBody.getMood());
        note.setFocusLevel(requestBody.getFocusLevel());
        Date now = new Date();
        note.setCreatedAt(now);
        note.setUpdatedAt(now);

        LearningNote saved = learningNoteService.createNote(note);
        return ResponseEntity.ok(saved);
    }

    private String normalizeStageType(String rawStageType) {
        if (rawStageType == null || rawStageType.trim().isEmpty()) {
            return "general";
        }
        String normalized = rawStageType.trim().toLowerCase();
        return SUPPORTED_STAGE_TYPES.contains(normalized) ? normalized : "general";
    }

    private String deriveQuestionType(String normalizedStage) {
        if (normalizedStage == null) {
            return "concept";
        }
        switch (normalizedStage) {
            case "concept":
            case "analogy":
            case "overview":
            case "usage":
            case "warmup":
            case "general":
                return "concept";
            case "example":
            case "practice":
            case "coding":
            case "visualization":
                return "practice";
            case "quiz":
                return "quiz";
            case "reflection":
                return "reflection";
            default:
                return "concept";
        }
    }

    /**
     * 删除学习笔记
     */
    @DeleteMapping("/training/notes/{noteId}")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> deleteLearningNote(@PathVariable Long noteId,
                                                                  HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        boolean deleted = learningNoteService.deleteNote(noteId, user.getId());
        return ResponseEntity.ok(Collections.singletonMap("success", deleted));
    }

    // ========== 错题集相关接口 ==========

    /**
     * 添加错题到错题集
     */
    @PostMapping("/wrong-questions")
    @ResponseBody
    public ResponseEntity<?> addWrongQuestion(@RequestBody WrongQuestionRequest request,
                                              HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        
        if (request == null || request.getQuestionId() == null) {
            return ResponseEntity.badRequest().body(Collections.singletonMap("message", "题目ID不能为空"));
        }
        
        try {
            UserWrongQuestion wrongQuestion = userWrongQuestionService.addWrongQuestion(
                user.getId(),
                request.getQuestionId(),
                request.getPointId(),
                request.getPackageId(),
                request.getUserAnswer()
            );
            return ResponseEntity.ok(wrongQuestion);
        } catch (Exception e) {
            log.error("添加错题失败，用户ID: {}, 题目ID: {}", user.getId(), request.getQuestionId(), e);
            return ResponseEntity.status(500).body(Collections.singletonMap("message", "添加错题失败: " + e.getMessage()));
        }
    }

    /**
     * 获取用户的错题集
     */
    @GetMapping("/wrong-questions")
    @ResponseBody
    public ResponseEntity<?> getWrongQuestions(
            @RequestParam(required = false) Long pointId,
            @RequestParam(required = false, defaultValue = "false") Boolean unmasteredOnly,
            @RequestParam(required = false, defaultValue = "false") Boolean dueForReview,
            HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        
        try {
            List<UserWrongQuestion> wrongQuestions;
            if (dueForReview) {
                // 获取需要复习的错题
                wrongQuestions = userWrongQuestionService.getDueForReviewQuestions(user.getId());
            } else if (pointId != null) {
                // 获取指定知识点的错题
                wrongQuestions = userWrongQuestionService.getWrongQuestionsByPoint(user.getId(), pointId);
            } else if (unmasteredOnly) {
                // 获取未掌握的错题
                wrongQuestions = userWrongQuestionService.getUnmasteredQuestions(user.getId());
            } else {
                // 获取所有错题
                wrongQuestions = userWrongQuestionService.getUserWrongQuestions(user.getId());
            }
            
            Map<String, Object> response = new HashMap<>();
            response.put("wrongQuestions", wrongQuestions);
            response.put("total", wrongQuestions.size());
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("获取错题集失败，用户ID: {}", user.getId(), e);
            return ResponseEntity.status(500).body(Collections.singletonMap("message", "获取错题集失败: " + e.getMessage()));
        }
    }

    /**
     * 标记错题为已掌握
     */
    @PostMapping("/wrong-questions/{questionId}/master")
    @ResponseBody
    public ResponseEntity<?> markAsMastered(@PathVariable Long questionId,
                                            HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        
        try {
            boolean success = userWrongQuestionService.markAsMastered(user.getId(), questionId);
            return ResponseEntity.ok(Collections.singletonMap("success", success));
        } catch (Exception e) {
            log.error("标记错题为已掌握失败，用户ID: {}, 题目ID: {}", user.getId(), questionId, e);
            return ResponseEntity.status(500).body(Collections.singletonMap("message", "操作失败: " + e.getMessage()));
        }
    }

    /**
     * 从错题集中移除题目
     */
    @DeleteMapping("/wrong-questions/{questionId}")
    @ResponseBody
    public ResponseEntity<?> removeFromWrongQuestions(@PathVariable Long questionId,
                                                       HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        
        try {
            boolean success = userWrongQuestionService.removeFromWrongQuestions(user.getId(), questionId);
            return ResponseEntity.ok(Collections.singletonMap("success", success));
        } catch (Exception e) {
            log.error("从错题集移除失败，用户ID: {}, 题目ID: {}", user.getId(), questionId, e);
            return ResponseEntity.status(500).body(Collections.singletonMap("message", "操作失败: " + e.getMessage()));
        }
    }

    /**
     * 更新练习记录（答对或答错）
     */
    @PostMapping("/wrong-questions/{questionId}/practice")
    @ResponseBody
    public ResponseEntity<?> updatePracticeRecord(@PathVariable Long questionId,
                                                  @RequestParam Boolean isCorrect,
                                                  HttpSession session) {
        UserResponse user = getSessionUser(session);
        if (user == null) {
            return ResponseEntity.status(401).body(Collections.singletonMap("message", "未登录"));
        }
        
        try {
            userWrongQuestionService.updatePracticeRecord(user.getId(), questionId, isCorrect);
            return ResponseEntity.ok(Collections.singletonMap("success", true));
        } catch (Exception e) {
            log.error("更新练习记录失败，用户ID: {}, 题目ID: {}", user.getId(), questionId, e);
            return ResponseEntity.status(500).body(Collections.singletonMap("message", "操作失败: " + e.getMessage()));
        }
    }

    // 在KnowledgeController.java中添加以下方法

    /**
     * 获取知识分类列表API - 支持分页
     */
    @GetMapping("/api/classes")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> getClasses(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "12") int size,
            HttpSession session) {

        log.info("获取知识分类列表，页码: {}, 每页大小: {}", page, size);
        Long userId = Optional.ofNullable(getSessionUser(session))
                .map(UserResponse::getId)
                .orElse(null);

        try {
            Page<KnowledgeClass> classPage = knowledgeClassService.getClassesWithPagination(page, size);

            List<Map<String, Object>> items = classPage.getResult().stream()
                    .map(cls -> buildClassCard(cls, userId))
                    .collect(Collectors.toList());

            Map<String, Object> response = new HashMap<>();
            response.put("items", items);
            response.put("totalPages", classPage.getPages());
            response.put("currentPage", classPage.getPageNum());
            response.put("totalItems", classPage.getTotal());
            response.put("pageSize", size);

            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("获取分类列表失败", e);
            return ResponseEntity.status(500).body(Collections.singletonMap("error", "获取分类列表失败"));
        }
    }

    /**
     * 获取指定分类下的模块列表API - 支持分页
     */
    @GetMapping("/api/classes/{classId}/modules")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> getModulesByClass(
            @PathVariable Long classId,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "12") int size,
            HttpSession session) {

        log.info("获取分类 {} 下的模块列表，页码: {}, 每页大小: {}", classId, page, size);
        Long userId = Optional.ofNullable(getSessionUser(session))
                .map(UserResponse::getId)
                .orElse(null);

        try {
            // 调用服务层获取指定分类下的模块分页数据
            Page<KnowledgeModule> modulePage = knowledgeModuleService.getModulesByClassIdWithPagination(classId, page, size);

            List<Map<String, Object>> items = modulePage.getResult().stream()
                    .map(module -> buildModuleCard(module, userId))
                    .collect(Collectors.toList());

            Map<String, Object> response = new HashMap<>();
            response.put("items", items);
            response.put("totalPages", modulePage.getPages());
            response.put("currentPage", modulePage.getPageNum());
            response.put("totalItems", modulePage.getTotal());
            response.put("pageSize", size);

            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("获取模块列表失败", e);
            return ResponseEntity.status(500).body(Collections.singletonMap("error", "获取模块列表失败"));
        }
    }

    /**
     * 获取指定模块下的知识包列表API - 支持分页
     */
    @GetMapping("/api/modules/{moduleId}/packages")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> getPackagesByModule(
            @PathVariable Long moduleId,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "12") int size,
            HttpSession session) {

        log.info("获取模块 {} 下的知识包列表，页码: {}, 每页大小: {}", moduleId, page, size);
        Long userId = Optional.ofNullable(getSessionUser(session))
                .map(UserResponse::getId)
                .orElse(null);

        try {
            // 调用服务层获取指定模块下的知识包分页数据
            Page<KnowledgePackageDTO> packagePage = knowledgePackageService.getPackagesByModuleWithPagination(moduleId, page, size);

            List<Map<String, Object>> items = packagePage.getResult().stream()
                    .map(pkg -> buildPackageCard(pkg, userId))
                    .collect(Collectors.toList());
            if (userId != null) {
                applySequentialPackageLocks(items);
            } else if (!items.isEmpty()) {
                items.get(0).put("locked", false);
            }

            Map<String, Object> response = new HashMap<>();
            response.put("items", items);
            response.put("totalPages", packagePage.getPages());
            response.put("currentPage", packagePage.getPageNum());
            response.put("totalItems", packagePage.getTotal());
            response.put("pageSize", size);

            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("获取知识包列表失败", e);
            return ResponseEntity.status(500).body(Collections.singletonMap("error", "获取知识包列表失败"));
        }
    }

    /**
     * 获取指定知识包下的知识点列表API - 支持分页
     */
    @GetMapping("/api/packages/{packageId}/points")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> getPointsByPackage(
            @PathVariable Long packageId,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "12") int size,
            HttpSession session) {

        log.info("获取知识包 {} 下的知识点列表，页码: {}, 每页大小: {}", packageId, page, size);
        Long userId = Optional.ofNullable(getSessionUser(session))
                .map(UserResponse::getId)
                .orElse(null);

        try {
            // 调用服务层获取指定知识包下的知识点分页数据
            Page<KnowledgePointDTO> pointPage = knowledgePointService.getPointsByPackageIdWithPagination(packageId, page, size);

            List<Map<String, Object>> items = pointPage.getResult().stream()
                    .map(point -> buildPointCard(point, userId))
                    .collect(Collectors.toList());
            if (userId != null) {
                applySequentialPointLocks(items);
            } else if (!items.isEmpty()) {
                items.get(0).put("locked", false);
            }

            Map<String, Object> response = new HashMap<>();
            response.put("items", items);
            response.put("totalPages", pointPage.getPages());
            response.put("currentPage", pointPage.getPageNum());
            response.put("totalItems", pointPage.getTotal());
            response.put("pageSize", size);

            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("获取知识点列表失败", e);
            return ResponseEntity.status(500).body(Collections.singletonMap("error", "获取知识点列表失败"));
        }
    }
    /**
     * 完成学习API
     */
    @PostMapping("/training/complete")
    @ResponseBody
    public String completeLearning(@RequestParam Long pointId,
                                   @RequestParam Integer rating,
                                   @RequestParam String notes) {
        log.info("完成学习，知识点ID: {}, 评分: {}, 笔记: {}", pointId, rating, notes);
        // 这里可以调用学习记录服务保存学习完成记录
        return "success";
    }

    private Map<String, Object> buildClassCard(KnowledgeClass cls, Long userId) {
        Map<String, Object> item = new HashMap<>();
        item.put("id", cls.getId());
        item.put("name", cls.getName());
        item.put("description", defaultDescription(cls.getDescription()));
        item.put("moduleCount", knowledgeModuleService.countByClassId(cls.getId()));
        int actualSeconds = (userId == null) ? 0 : userProgressService.getClassDurationSeconds(userId, cls.getId());
        item.put("actualDurationSeconds", actualSeconds);
        item.put("actualDurationText", formatDurationText(actualSeconds));
        item.put("totalDuration", toRoundedMinutes(actualSeconds));
        item.put("progress", 0);
        return item;
    }

    private Map<String, Object> buildModuleCard(KnowledgeModule module, Long userId) {
        Map<String, Object> item = new HashMap<>();
        item.put("id", module.getId());
        item.put("name", module.getName());
        item.put("description", defaultDescription(module.getDescription()));
        item.put("packageCount", knowledgePackageService.countByModule(module.getId()));
        int actualSeconds = (userId == null) ? 0 : userProgressService.getModuleDurationSeconds(userId, module.getId());
        item.put("actualDurationSeconds", actualSeconds);
        item.put("actualDurationText", formatDurationText(actualSeconds));
        item.put("totalDuration", toRoundedMinutes(actualSeconds));
        item.put("progress", 0);
        return item;
    }

    private Map<String, Object> buildPackageCard(KnowledgePackageDTO dto, Long userId) {
        Map<String, Object> item = new HashMap<>();
        item.put("id", dto.getId());
        item.put("name", dto.getName());
        item.put("description", defaultDescription(dto.getDescription()));
        item.put("moduleId", dto.getModuleId());
        item.put("moduleName", dto.getModuleName());
        int totalPoints = dto.getPointCount() == null ? 0 : dto.getPointCount();
        item.put("pointCount", totalPoints);
        item.put("totalExperience", dto.getTotalExperience());
        item.put("beginnerPoints", dto.getBeginnerPoints());
        item.put("intermediatePoints", dto.getIntermediatePoints());
        item.put("advancedPoints", dto.getAdvancedPoints());

        int actualSeconds = (userId == null) ? 0 : userProgressService.getPackageDurationSeconds(userId, dto.getId());
        item.put("actualDurationSeconds", actualSeconds);
        item.put("actualDurationText", formatDurationText(actualSeconds));
        int estimatedMinutes = dto.getTotalDuration() == null ? 0 : dto.getTotalDuration();
        item.put("estimatedDuration", estimatedMinutes);
        item.put("totalDuration", estimatedMinutes);

        UserPackageProgress progress = (userId == null) ? null : userProgressService.getPackageProgress(userId, dto.getId());
        int completedPoints = progress != null && progress.getCompletedPoints() != null ? progress.getCompletedPoints() : 0;
        boolean finishedAllPoints = totalPoints > 0 && completedPoints >= totalPoints;
        String status = progress != null && progress.getStatus() != null ? progress.getStatus() : (completedPoints > 0 ? "IN_PROGRESS" : "NOT_STARTED");
        boolean completed = finishedAllPoints || "COMPLETED".equalsIgnoreCase(status);
        int progressPercent = totalPoints > 0 ? Math.min(100, (int) Math.round((completedPoints * 100.0) / totalPoints)) : 0;

        item.put("completedPoints", completedPoints);
        item.put("progress", progressPercent);
        item.put("status", status);
        item.put("statusLabel", getPackageStatusLabel(status, finishedAllPoints));
        item.put("completed", completed);
        item.put("locked", false);
        return item;
    }

    private Map<String, Object> buildPointCard(KnowledgePointDTO point, Long userId) {
        String title = (point.getTitle() == null || point.getTitle().trim().isEmpty()) ? "未命名知识点" : point.getTitle();
        Map<String, Object> item = new HashMap<>();
        item.put("id", point.getId());
        item.put("name", title);
        item.put("title", title);
        item.put("description", defaultDescription(point.getDescription()));
        item.put("duration", point.getBaseDuration() == null ? 0 : point.getBaseDuration());
        item.put("baseDuration", point.getBaseDuration());
        item.put("difficulty", normalizeDifficulty(point.getDifficultyLevel()));
        item.put("difficultyLevel", point.getDifficultyLevel());
        item.put("difficultyLabel", getDifficultyLabel(point.getDifficultyLevel()));
        item.put("knowledgeType", point.getKnowledgeType());
        item.put("packageId", point.getPackageId());

        int actualSeconds = (userId == null) ? 0 : userProgressService.getPointDurationSeconds(userId, point.getId());
        item.put("actualDurationSeconds", actualSeconds);
        item.put("actualDurationText", formatDurationText(actualSeconds));

        UserPointProgress progress = (userId == null) ? null : userProgressService.getPointProgress(userId, point.getId());
        String status = progress != null && progress.getStatus() != null ? progress.getStatus() : (actualSeconds > 0 ? "IN_PROGRESS" : "NOT_STARTED");
        boolean completed = "COMPLETED".equalsIgnoreCase(status);
        boolean started = completed || "IN_PROGRESS".equalsIgnoreCase(status) || actualSeconds > 0;
        int baseSeconds = point.getBaseDuration() == null ? 0 : point.getBaseDuration() * 60;
        int progressPercent;
        if (completed) {
            progressPercent = 100;
        } else if (baseSeconds > 0 && actualSeconds > 0) {
            progressPercent = Math.min(95, (int) Math.round((actualSeconds * 100.0) / baseSeconds));
        } else {
            progressPercent = 0;
        }

        item.put("progress", progressPercent);
        item.put("status", status);
        item.put("statusLabel", getStatusLabel(status));
        item.put("completed", completed);
        item.put("started", started);
        item.put("locked", false);
        item.put("order", point.getOrderNum());
        return item;
    }

    private String defaultDescription(String text) {
        return (text == null || text.trim().isEmpty()) ? "暂无描述" : text;
    }

    private String normalizeDifficulty(String difficulty) {
        if (difficulty == null) {
            return "unknown";
        }
        return difficulty.trim().toLowerCase();
    }

    private int toRoundedMinutes(int seconds) {
        if (seconds <= 0) {
            return 0;
        }
        return (int) Math.max(1, Math.round(seconds / 60.0));
    }

    private String formatDurationText(int seconds) {
        if (seconds <= 0) {
            return "0 分钟";
        }
        if (seconds < 3600) {
            return toRoundedMinutes(seconds) + " 分钟";
        }
        double hours = seconds / 3600.0;
        if (hours >= 10) {
            return Math.round(hours) + " 小时";
        }
        return String.format("%.1f 小时", hours);
    }

    private String getDifficultyLabel(String difficulty) {
        if (difficulty == null) {
            return "未知";
        }
        switch (difficulty.trim().toUpperCase()) {
            case "BEGINNER":
                return "初级";
            case "INTERMEDIATE":
                return "中级";
            case "ADVANCED":
                return "高级";
            default:
                return "未知";
        }
    }

    private void applySequentialPointLocks(List<Map<String, Object>> items) {
        boolean allowNext = true;
        for (int i = 0; i < items.size(); i++) {
            Map<String, Object> item = items.get(i);
            if (i == 0) {
                item.put("locked", false);
                item.put("unlockHint", "");
                if (!Boolean.TRUE.equals(item.getOrDefault("completed", false))) {
                    allowNext = false;
                }
                continue;
            }
            boolean completed = Boolean.TRUE.equals(item.getOrDefault("completed", false));
            item.put("locked", !allowNext);
            item.put("unlockHint", allowNext ? "" : "完成上一知识点后自动解锁");
            if (!completed && allowNext) {
                allowNext = false;
            }
        }
    }

    private void applySequentialPackageLocks(List<Map<String, Object>> items) {
        boolean allowNext = true;
        for (int i = 0; i < items.size(); i++) {
            Map<String, Object> item = items.get(i);
            if (i == 0) {
                item.put("locked", false);
                item.put("unlockHint", "");
                if (!Boolean.TRUE.equals(item.getOrDefault("completed", false))) {
                    allowNext = false;
                }
                continue;
            }
            boolean completed = Boolean.TRUE.equals(item.getOrDefault("completed", false));
            item.put("locked", !allowNext);
            item.put("unlockHint", allowNext ? "" : "完成上一知识包后自动解锁");
            if (!completed && allowNext) {
                allowNext = false;
            }
        }
    }

    private String getPackageStatusLabel(String status, boolean finishedAllPoints) {
        if (finishedAllPoints && (status == null || !"COMPLETED".equalsIgnoreCase(status))) {
            return "待闯关";
        }
        return getStatusLabel(status);
    }

    private String getStatusLabel(String status) {
        if (status == null) {
            return "未开始";
        }
        switch (status.trim().toUpperCase()) {
            case "READY_FOR_GATEWAY":
                return "待闯关";
            case "IN_PROGRESS":
                return "学习中";
            case "COMPLETED":
                return "已完成";
            default:
                return "未开始";
        }
    }

    private UserResponse getSessionUser(HttpSession session) {
        Object userObj = session.getAttribute("user");
        if (userObj instanceof UserResponse) {
            return (UserResponse) userObj;
        }
        return null;
    }

    private List<LearningTask> buildTaskPlan(KnowledgePointDTO point) {
        List<LearningTask> tasks = new ArrayList<>();
        if (point == null) {
            return tasks;
        }

        String type = point.getKnowledgeType() != null ? point.getKnowledgeType() : determineKnowledgeType(point);
        String difficulty = point.getDifficultyLevel() != null ? point.getDifficultyLevel() : "medium";
        int baseDuration = point.getBaseDuration() == null ? 15 : point.getBaseDuration();
        int baseExp = point.getBaseExperience() == null ? 60 : point.getBaseExperience();

        long baseId = point.getId() == null ? System.currentTimeMillis() : point.getId() * 10;
        Date now = new Date();

        tasks.add(createTask(baseId + 1,
                point.getTitle() + " · 预热理解",
                "拆解知识背景，锁定高频考点与业务价值。",
                "warmup",
                difficulty,
                (int) Math.round(baseExp * 0.25),
                Math.max(5, (int) Math.round(baseDuration * 0.4)),
                type,
                point.getContentUrl(),
                now));

        tasks.add(createTask(baseId + 2,
                point.getTitle() + " · 深度实践",
                "结合示例代码、可视化和即时测验完成动手演练。",
                "practice",
                difficulty,
                (int) Math.round(baseExp * 0.45),
                Math.max(8, (int) Math.round(baseDuration * 0.8)),
                type,
                point.getContentUrl(),
                now));

        tasks.add(createTask(baseId + 3,
                point.getTitle() + " · 巩固输出",
                "整理学习笔记、记录疑问并完成随堂测验。",
                "reflection",
                difficulty,
                (int) Math.round(baseExp * 0.30),
                Math.max(6, (int) Math.round(baseDuration * 0.6)),
                type,
                point.getContentUrl(),
                now));

        return tasks;
    }

    private LearningTask createTask(Long id,
                                    String title,
                                    String description,
                                    String category,
                                    String difficulty,
                                    Integer experience,
                                    Integer duration,
                                    String type,
                                    String contentUrl,
                                    Date timestamp) {
        LearningTask task = new LearningTask();
        task.setId(id);
        task.setTitle(title);
        task.setDescription(description);
        task.setCategory(category);
        task.setDifficulty(difficulty);
        task.setExperienceReward(experience);
        task.setEstimatedDuration(duration);
        task.setType(type);
        task.setContentUrl(contentUrl);
        task.setCreatedTime(timestamp);
        task.setUpdatedTime(timestamp);
        return task;
    }

    // ========== 辅助方法 ==========

    /**
     * 智能判断知识点类型
     */
    private String determineKnowledgeType(KnowledgePointDTO point) {
        // 如果已经设置了类型，直接返回
        if (point.getKnowledgeType() != null && !point.getKnowledgeType().isEmpty()) {
            return point.getKnowledgeType();
        }

        // 根据标题和描述智能判断类型
        String title = point.getTitle().toLowerCase();
        String description = point.getDescription().toLowerCase();

        // 代码实践型关键词
        if (title.contains("实现") || title.contains("编写") || title.contains("代码") ||
                title.contains("算法") || title.contains("练习") || title.contains("实战") ||
                description.contains("代码") || description.contains("实现") || description.contains("编写")) {
            return "代码实践型";
        }

        // API学习型关键词
        if (title.contains("api") || title.contains("使用") || title.contains("方法") ||
                title.contains("函数") || title.contains("类") || title.contains("接口") ||
                description.contains("api") || description.contains("调用") || description.contains("方法")) {
            return "API学习型";
        }

        // 默认为概念理解型
        return "概念理解型";
    }


    /**
     * 动态生成学习内容（备用方案）
     */
    private Map<String, Object> generateDynamicLearningContent(Long pointId) {
        try {
            KnowledgePointDTO point = knowledgePointService.getPointDetail(pointId);
            Map<String, Object> content = new HashMap<>();

            content.put("title", point.getTitle());
            content.put("description", point.getDescription());
            content.put("conceptContent", generateConceptContent(point));
            content.put("analogyContent", generateAnalogyContent(point));
            content.put("exampleContent", generateExampleContent(point));
            content.put("knowledgeType", determineKnowledgeType(point));

            return content;
        } catch (Exception e) {
            log.error("动态生成学习内容失败", e);
            return createFallbackContent();
        }
    }

    /**
     * 创建备用内容
     */
    private Map<String, Object> createFallbackContent() {
        Map<String, Object> content = new HashMap<>();
        content.put("title", "学习内容");
        content.put("description", "这是学习内容的描述");
        content.put("conceptContent", "这是概念解析内容");
        content.put("analogyContent", "这是生活化类比内容");
        content.put("exampleContent", "这是实际示例内容");
        content.put("knowledgeType", "概念理解型");
        return content;
    }

    /**
     * 生成概念内容
     */
    private String generateConceptContent(KnowledgePointDTO point) {
        String title = point.getTitle();

        if (title.contains("IoC") || title.contains("依赖注入")) {
            return "IoC（控制反转）是一种设计原则，用来将对象的创建和绑定从使用它们的代码中解耦。在传统的程序设计中，我们直接在代码中创建对象，而使用IoC后，对象的创建和管理由容器负责，我们只需要定义好依赖关系即可。";
        } else if (title.contains("多线程")) {
            return "多线程允许程序同时执行多个任务，提高CPU利用率和程序响应性。通过创建多个线程，可以让程序在执行耗时操作时仍能响应用户交互，或者利用多核CPU的优势并行处理任务。";
        } else if (title.contains("集合")) {
            return "集合框架提供了一系列用于存储和操作数据组的数据结构。不同的集合类有不同的特性和适用场景，如List适合有序存储，Set适合去重存储，Map适合键值对存储。";
        }
        return "让我们深入理解 " + title + " 的核心概念和基本原理。这个概念在软件开发中扮演着重要角色，掌握它对于提升编程能力至关重要。";
    }

    /**
     * 生成类比内容
     */
    private String generateAnalogyContent(KnowledgePointDTO point) {
        String title = point.getTitle();

        if (title.contains("IoC") || title.contains("依赖注入")) {
            return "IoC就像餐厅的点餐系统：传统方式是你自己去厨房点菜（直接创建对象），而IoC是你告诉服务员（容器），服务员帮你协调一切。你不需要关心菜是怎么做的，只需要享受美食（使用对象）。";
        } else if (title.contains("多线程")) {
            return "多线程就像厨房里的多位厨师：单线程就像只有一位厨师，他必须一道菜一道菜地做；而多线程就像有多位厨师，他们可以同时准备不同的菜品，大大提高效率。";
        } else if (title.contains("集合")) {
            return "集合就像不同的储物容器：List就像书架，书按顺序摆放；Set就像储物箱，每个物品只放一次；Map就像带标签的储物柜，通过标签快速找到物品。";
        }
        return "想象一下 " + title + " 就像日常生活中的某个场景，通过这种类比可以帮助你更好地理解这个抽象概念的实际应用和意义。";
    }

    /**
     * 生成示例内容
     */
    private String generateExampleContent(KnowledgePointDTO point) {
        String title = point.getTitle();

        if (title.contains("IoC") || title.contains("依赖注入")) {
            return "在实际项目中，IoC容器负责管理所有Bean的生命周期。我们不需要手动创建对象，而是通过配置或注解让容器自动注入依赖。这种模式大大降低了代码的耦合度，提高了可测试性。";
        } else if (title.contains("多线程")) {
            return "多线程编程可以显著提高程序性能。在实际应用中，我们可以使用线程池来管理线程，避免频繁创建和销毁线程的开销，同时确保系统资源的合理利用。";
        } else if (title.contains("集合")) {
            return "Java集合框架提供了丰富的数据结构。在实际开发中，我们需要根据具体需求选择合适的集合类型，比如需要快速查找时使用HashMap，需要保持顺序时使用ArrayList。";
        }
        return "让我们通过实际示例来理解 " + title + " 的具体应用。这个示例展示了该概念在实际项目中的典型用法。";
    }

    /**
     * 规范化题目类型用于过滤
     * 将各种题目类型统一为标准格式
     */
    private String normalizeQuestionTypeForFilter(String questionType) {
        if (questionType == null || questionType.trim().isEmpty()) {
            return "single";
        }
        
        String normalized = questionType.toLowerCase().trim();
        
        // 单选题
        if (normalized.contains("single") || normalized.equals("choice")) {
            return "single";
        }
        
        // 多选题
        if (normalized.contains("multiple")) {
            return "multiple";
        }
        
        // 填空题
        if (normalized.contains("fill") || normalized.contains("blank")) {
            return "fill-blank";
        }
        
        // 判断题
        if (normalized.contains("true") || normalized.contains("false") || normalized.contains("judge")) {
            return "true-false";
        }
        
        return normalized;
    }
}